package com.tx.websocket.demo.shiro;

import com.tx.websocket.demo.redis.CommonConfig;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

public class ShiroCache<K, V> implements Cache<K, V> {

    private RedisTemplate<K, V> redisTemplate;

    private CommonConfig commonConfig;

    private static final String REDIS_SESSION_CACHE = "shiro-cache:";
    private String cacheKey;

    private static final Logger logger = LoggerFactory.getLogger(ShiroCache.class);

    public ShiroCache(String name, RedisTemplate<K, V> redisTemplate, CommonConfig config) {
        this.cacheKey = REDIS_SESSION_CACHE + name + ":";
        this.redisTemplate = redisTemplate;
        this.commonConfig = config;
    }

    @Override
    public V get(K k) throws CacheException {
        V val;
        Object value = redisTemplate.opsForValue().get(getCacheKey(k));
        redisTemplate.expire(getCacheKey(k), commonConfig.getSessionTimeout(), TimeUnit.MINUTES);
        return (V) value;
    }

    @Override
    public V put(K k, V v) throws CacheException {
        V old = get(k);
        redisTemplate.opsForValue().set(getCacheKey(k), v, commonConfig.getSessionTimeout(), TimeUnit.MINUTES);
        return old;
    }

    @Override
    public V remove(K k) throws CacheException {
        V old = get(k);
        redisTemplate.delete(getCacheKey(k));
        return old;
    }

    @Override
    public void clear() throws CacheException {
        Set<K> keys = keys();
        redisTemplate.delete(keys);
    }

    @Override
    public int size() {
        return keys().size();
    }

    @Override
    public Set<K> keys() {
        return redisTemplate.keys(getCacheKey("*"));
    }

    @Override
    public Collection<V> values() {
        Set<K> set = keys();
        List<V> list = new ArrayList<>();
        for (K k : set) {
            list.add(get(k));
        }
        return list;
    }

    private K getCacheKey(Object k) {
        return (K) (this.cacheKey + k);
    }
}
